const MAXSIZE = 300;

/********经过以下连个函数就可以知道种别码对应的字符在符号表中的位置************/

/**
 * 根据种别码返回相应字符
 * @param {*} syn
 */
const SearchSyn = syn => {
    return SymbolTbl_Define[syn - 1];
};

const Prior_Position = ['+', '-', '*', '/', '(', ')', 'v', 'c', '=', '?', '#', '\0'];

/**
 * 搜索一个字符在优先符表中的位置
 * @param {*} ch
 */
const Search_Priority_Setxy = ch => {
    for (let i = 0; i < 11; i++) {
        if (ch == Prior_Position[i]) {
            return i + 1; //返回该位置
        }
    }
    return -1; //失败则提示错误
};
/*******************************************************************/

// const traverseStack = pointer => {
//     //若pointer<0则报错
//     if (pointer < 0) {
//         (cout << '已到栈底，溢出!') << endl;
//         exit(0); //退出，分析失败
//     }
//     if (pointer > top) {
//         (cout << '试图访问栈外元素，超界!') << endl;
//         exit(0); //退出，分析失败
//     }
//     return a[pointer]; //返回元素值
// };

let Resource_Symbol = [];

/**
 * 打印出当前堆栈和符号表的上下文
 * @param {*} Stack_Top
 * @param {*} Sym_Pointer
 */
const Print_Context = (Stack_Top, Sym_Pointer) => {
    // 当前堆栈的情况为：
    for (let i = 0; i <= Stack_Top; i++) {
        const { syn, value } = Resource_Symbol[i];
        // console.log(syn, value);
    }

    // 当前符号表情况为：
    // 计算符号表长度
    let sym_length = 1;
    for (let i = 1; symbol[i].syn !== 12; i++) {
        sym_length++;
    }
    for (let i = Sym_Pointer; i < sym_length; i++) {
        const { syn, value } = symbol[i];
        // console.log(syn, value);
    }
};

/**
 * 语法分析主程序
 */
const MainProc_Analysis = () => {
    let row_prior, column_prior; // 行列定位指针
    let Prior_Relation;          // 优先关系
    let sym_length;              // 符号表长度
    let pStackTop;               // 栈顶指针
    let pScanStack;              // 定义扫描堆栈指针
    let Statement_count = 0;     // 记录步骤序号

    // 现在的算法是
    // 1. 初始化栈，在栈中放入#(12，),然后开始分析
    // # --> [] --> ['#'] #入栈
    const temp_sym = new SymbolTable();
    temp_sym.syn = 12;
    temp_sym.value = -1;                // -1代表这个地方不需要
    Resource_Symbol.push(temp_sym);      // 将 # 压栈

    // eg: 输入 #a=1# --> 从第二个字符开始计算 a=1# 到最后#结束
    // 对symbol中的内容定义指针，并且略过第一个 #
    let Sym_scan_pointer = 1; // 从一开始
    sym_length = 1;
    for (let i = 1; symbol[i].syn !== 12; i++) {
        sym_length++;
    }
    // 执行完毕后 sym_length 为 4，不解之处~~

    // 判断符号表第一个元素的syn === 11 ? 即是否为clear
    // 清除语句，清除屏幕并且清空标号表中的值
    if (sym_length >= 3 && (symbol[1].syn == 11)) {
        Clear_IDentifier_Tbl();
        Clear_Symbol_Tbl();
        // Resource_Symbol.Clear_Stack();
        Resource_Symbol = [];
        // system("cls");
        // goto end;
    } else {
        // 下面比较栈中元素和符号表中元素的大小关系
        // pStackTop = Resource_Symbol.getTopPointer();
        pStackTop = Resource_Symbol.length - 1;
        pScanStack = pStackTop; // 扫描指针从栈顶开始
        Print_Context(pStackTop, Sym_scan_pointer);

        let exit = 0;
        while(true) {
            const symRow = SearchSyn(Resource_Symbol[pScanStack].syn);
            const symCol = SearchSyn(symbol[Sym_scan_pointer].syn);

            row_prior = Search_Priority_Setxy(symRow);      // 栈扫描指针
            column_prior = Search_Priority_Setxy(symCol);   // 符号表扫描指针
            Prior_Relation = PriorityTable[row_prior][column_prior];

            console.log(`${symRow} ${Prior_Relation} ${symCol}`);

            // 要是为小于或等于关系，则进栈    column_prior | row_prior 为 #
            if (Prior_Relation === '<' || (Prior_Relation === '=' && (column_prior !== 11 || row_prior !== 11))) {
                // 这里的等于关系可能为(N),v=,这两种关系
                Resource_Symbol.push(symbol[Sym_scan_pointer]); // 符号入栈
                pStackTop = Resource_Symbol.length - 1;         // 符号入栈后更新栈顶指针
                Sym_scan_pointer++;                             // 符号扫描指针向前移动

                pScanStack = pStackTop;                         // 栈扫描指针指向栈顶
                console.log(`${Statement_count++}.这里是小于、等于关系，压栈！`);
                Print_Context(pStackTop, Sym_scan_pointer);
            } else if (Prior_Relation === '=' && column_prior === 11 && row_prior === 11) {
                // 规约到最后#N,查看栈中是否满足情况
                // 输出N的数值并结束
                let final_value;
                const topPointer = Resource_Symbol.length - 1;
                if (Resource_Symbol[topPointer].syn === 13 && topPointer === 1) {
                    // 若满足#N的情况，则归约成功！
                    console.log(`${Statement_count++}.归约成功!!!`);
                    Print_Context(topPointer, Sym_scan_pointer);
                    final_value = Resource_Symbol[topPointer].value;
                    console.log(`最后的结果为: ${final_value}`);
                    Resource_Symbol = [];
                    break; // 退出大循环,唯一的正确出口
                } else {
                    console.log('句子错误，程序结束!');
                    exit = 1;
                    break;
                }
            } else if (Prior_Relation === '>') {
                // 注意下面两句话要放在循环之前!!!
                pStackTop = Resource_Symbol.length - 1; // 得到栈顶指针
                pScanStack = pStackTop;                 // 扫描指针从栈顶开始

                while(Prior_Relation === '>') {
                    // 若优先关系始终为大于，则堆栈指针前移，不断寻找可归约串
                    // 因为可归约串始终在栈顶的某段区域，那么就要试探着先看一下栈顶元素是不是
                    // 若栈顶元素可归约则直接归约为N(13,),否则查看栈顶方向的两个元素同样道理
                    // 因为文法的产生式右部最多只有三个元素，因此若是到了三个元素还没有搜索到
                    // 可归约串，则视为语法错误，停止分析
                    // 此处构建两个指针，一个既是栈顶指针，另一个为扫描可归约串的指针
                    // 这里不可能出现 '('

                    // 判断栈顶元素是否可归约
                    let judge;
                    judge = Resource_Symbol[pScanStack].syn;

                    console.log('-----judge: ', judge);

                    // 判断该种别码
                    // 若是单个变量或常量则规约为N
                    // 此处还要进行语义分析，对于标识符要查标识符表判断是否存在，若不存在则为错
                    if (judge === 9) {
                        if (idTbl[Resource_Symbol[pScanStack].value].value === -1) {
                            console.log('此变量可能没被定义，按照-1处理');
                        }

                        // 若该变量的符号表入口地址为负值，则说明查无此元素，则认为语法出错，变量未定义
                        if (Resource_Symbol[pScanStack].value < 0) {
                            console.log('该变量未定义');
                            exit = 1;
                            break;
                        } else {
                            // 否则将该标识符规约为N
                            const temp_sym = new SymbolTable();
                            temp_sym.syn = 13;
                            // 获得符号表中关于该变量的值
                            temp_sym.value = idTbl[Resource_Symbol[pScanStack].value].value;

                            Resource_Symbol.pop();                       // 先弹出栈顶元素
                            Resource_Symbol.push(temp_sym);              // 将N压栈
                            pStackTop = Resource_Symbol.length - 1;      // 得到变动后的栈顶指针，始终指向栈顶

                            // 扫描指针要前移，因为要分析终结符，而栈顶已变成非终结符
                            pScanStack = Resource_Symbol.length - 1 - 1; // 进行下一次判断
                            if (pScanStack < 0) {
                                cout << "句子错误，程序结束!" << endl;
                                console.log('句子错误，程序结束!');
                                exit = 1;
                                break;
                            }
                            row_prior = Search_Priority_Setxy(SearchSyn(Resource_Symbol[pScanStack].syn));
                            Prior_Relation = PriorityTable[row_prior][column_prior]; // 此处列是不会变的，因为大于关系
                            console.log(`${Statement_count++}.此处将变量规约为N`);
                            Print_Context(Resource_Symbol.length - 1, Sym_scan_pointer);
                        }
                    } else if (judge === 10) {
                        // 若是常量，则要记住该常量值以便进行规约时赋值
                        const temp_sym = new SymbolTable();
                        temp_sym.syn = 13;
                        temp_sym.value = Resource_Symbol[pScanStack].value;

                        Resource_Symbol.pop();                               // 先弹出栈顶元素
                        Resource_Symbol.push(temp_sym);                      // 将N压栈
                        pStackTop = Resource_Symbol.length - 1;
                        pScanStack = Resource_Symbol.length - 1 - 1;         // 进行下一次判断
                        if (pScanStack < 0) {
                            console.log('句子错误，程序结束!');
                            exit = 1;
                            break;
                        }
                        const symRow = SearchSyn(Resource_Symbol[pScanStack].syn);
                        row_prior = Search_Priority_Setxy(symRow);
                        Prior_Relation = PriorityTable[row_prior][column_prior]; // 同标识符
                        console.log(`${symRow} ${Prior_Relation} ${symCol}`)
                        console.log(`${Statement_count++}.此处将常量规约为N`);
                        Print_Context(Resource_Symbol.length - 1, Sym_scan_pointer);
                    } else if (judge === 1) {
                        // v=E,此时要进行赋值操作，然后规约
                        // 一定为9
                        if (Resource_Symbol[pScanStack - 1].syn === 9) {
                            // 若前面为标识符则正确，应该为该变量赋值

                            // 语义分析
                            idTbl[Resource_Symbol[pScanStack - 1].value].value = Resource_Symbol[Resource_Symbol.length - 1].value; // 此时栈顶必为E
                            const temp_sym = new SymbolTable();
                            temp_sym.syn = 13;
                            temp_sym.value = Resource_Symbol[Resource_Symbol.length - 1].value;   // N中的值永远为真正的值，而不是地址

                            // 开始归约
                            Resource_Symbol.pop();
                            Resource_Symbol.pop();
                            Resource_Symbol.pop();           // 弹出三个元素
                            Resource_Symbol.push(temp_sym);  // 规约为N
                            pStackTop = Resource_Symbol.length - 1;
                            pScanStack = pStackTop - 1;     // 始终指向规约后的第一个非终结符
                            if (pScanStack < 0) {
                                console.log('句子错误，程序结束!');
                                exit = 1;
                                break;
                            }
                            row_prior = Search_Priority_Setxy(SearchSyn(Resource_Symbol[pScanStack].syn));
                            Prior_Relation = PriorityTable[row_prior][column_prior]; // 进行下次判断
                            console.log(`${Statement_count++}.此处遇到等号要将v=E规约为N,程序面临结束！`);
                            Print_Context(Resource_Symbol.length - 1, Sym_scan_pointer);
                        } else {
                            console.log('等号前面应该有变量！');
                            exit = 1;
                            break;
                        }
                    } else if (judge === 3) {
                        // +，此时栈顶应该为N+N的形式
                        // 只需把两个数相加，并且把结果放在N中即可
                        // 一定为13 若前面为N并且栈顶为N则 正确
                        if (Resource_Symbol[pScanStack - 1].syn == 13 && Resource_Symbol[pScanStack + 1].syn == 13) {
                            // 语义分析,newtemp()
                            const temp_sym = new SymbolTable();
                            temp_sym.syn = 13;
                            // N中的值永远为真正的值，而不是地址
                            temp_sym.value = Resource_Symbol[pScanStack - 1].value + Resource_Symbol[pScanStack + 1].value;
                            // 开始归约
                            Resource_Symbol.pop();
                            Resource_Symbol.pop();
                            Resource_Symbol.pop();               // 弹出三个元素
                            Resource_Symbol.push(temp_sym);      // 规约为N
                            pStackTop = Resource_Symbol.length - 1;
                            pScanStack = pStackTop - 1;         // 始终指向规约后的第一个非终结符
                            if (pScanStack < 0) {
                                console.log('句子错误，程序结束!');
                                exit = 1;
                                break;
                            }
                            row_prior = Search_Priority_Setxy(SearchSyn(Resource_Symbol[pScanStack].syn));
                            Prior_Relation = PriorityTable[row_prior][column_prior]; // 进行下次判断
                            console.log(`${Statement_count++}.此处为加法运算，将N+N规约为N,并进行语义分析！`);
                            Print_Context(Resource_Symbol.length - 1, Sym_scan_pointer);
                        } else {
                            console.log('加号两边应该都有变量，句子错误！');
                            exit = 1;
                            break;
                        }
                    } else if (judge === 4) {
                        // - 一定为13
                        // 若前面为N并且栈顶为N则正确   N-N
                        if (Resource_Symbol[pScanStack - 1].syn == 13 && Resource_Symbol[pScanStack + 1].syn == 13) {
                            // 语义分析,newtemp()
                            const temp_sym = new SymbolTable();
                            temp_sym.syn = 13;
                            // 注意运算的顺序
                            temp_sym.value = Resource_Symbol[pScanStack - 1].value - Resource_Symbol[pScanStack + 1].value; // N中的值永远为真正的值，而不是地址
                            // 开始归约
                            Resource_Symbol.pop();
                            Resource_Symbol.pop();
                            Resource_Symbol.pop();           // 弹出三个元素
                            Resource_Symbol.push(temp_sym);  // 规约为N
                            pStackTop = Resource_Symbol.length - 1;
                            pScanStack = pStackTop - 1;     // 始终指向规约后的第一个非终结符
                            if (pScanStack < 0) {
                                console.log('句子错误，程序结束!');
                                exit = 1;
                                break;
                            }
                            row_prior = Search_Priority_Setxy(SearchSyn(Resource_Symbol[pScanStack].syn));
                            Prior_Relation = PriorityTable[row_prior][column_prior]; // 进行下次判断
                            console.log(`${Statement_count++}.此处为减法运算，将N-N规约为N,并进行语义分析！`);
                            Print_Context(Resource_Symbol.length - 1, Sym_scan_pointer);
                        } else {
                            console.log('减号两边应该都有变量，句子错误！');
                            exit = 1;
                            break;
                        }
                    } else if (judge === 5) {
                        // *
                        // 一定为13
                        if (Resource_Symbol[pScanStack - 1].syn == 13 && Resource_Symbol[pScanStack + 1].syn == 13) {
                            // 若前面为N并且栈顶为N则正确   N*N
                            // 语义分析,newtemp()
                            const temp_sym = new SymbolTable();
                            temp_sym.syn = 13;
                            // 注意运算的顺序
                            temp_sym.value = Resource_Symbol[pScanStack - 1].value * Resource_Symbol[pScanStack + 1].value; // N中的值永远为真正的值，而不是地址
                            // 开始归约
                            Resource_Symbol.pop();
                            Resource_Symbol.pop();
                            Resource_Symbol.pop();                   // 弹出三个元素
                            Resource_Symbol.push(temp_sym);          // 规约为N
                            pStackTop = Resource_Symbol.length - 1;
                            pScanStack = pStackTop - 1;             // 始终指向规约后的第一个非终结符
                            if (pScanStack < 0) {
                                console.log('句子错误，程序结束!');
                                exit = 1;
                                break;
                            }
                            row_prior = Search_Priority_Setxy(SearchSyn(Resource_Symbol[pScanStack].syn));
                            Prior_Relation = PriorityTable[row_prior][column_prior]; // 进行下次判断
                            console.log(`${Statement_count++}.此处为乘法运算，将N*N规约为N,并进行语义分析！`);
                            Print_Context(Resource_Symbol.length - 1, Sym_scan_pointer);
                        } else {
                            console.log('乘号两边应该都有变量，句子错误！');
                            exit = 1;
                            break;
                        }
                    } else if (judge === 6) {
                        // 除 /
                        // 一定为13
                        if (Resource_Symbol[pScanStack - 1].syn == 13 && Resource_Symbol[pScanStack + 1].syn == 13) {
                            // 若前面为N并且栈顶为N则正确   N*N
                            // 语义分析,newtemp()
                            const temp_sym = new SymbolTable();
                            temp_sym.syn = 13;
                            // 注意运算的顺序
                            temp_sym.value = Resource_Symbol[pScanStack - 1].value / Resource_Symbol[pScanStack + 1].value; // N中的值永远为真正的值，而不是地址
                            // 开始归约
                            Resource_Symbol.pop();
                            Resource_Symbol.pop();
                            Resource_Symbol.pop();               // 弹出三个元素
                            Resource_Symbol.push(temp_sym);      // 规约为N
                            pStackTop = Resource_Symbol.length - 1;
                            pScanStack = pStackTop - 1;         // 始终指向规约后的第一个非终结符
                            if (pScanStack < 0) {
                                console.log('句子错误，程序结束!');
                                exit = 1;
                                break;
                            }
                            row_prior = Search_Priority_Setxy(SearchSyn(Resource_Symbol[pScanStack].syn));
                            Prior_Relation = PriorityTable[row_prior][column_prior]; // 进行下次判断
                            console.log(`${Statement_count++}.此处为除法运算，将N/N规约为N,并进行语义分析！`);
                            Print_Context(Resource_Symbol.length - 1, Sym_scan_pointer);
                        } else {
                            console.log('除号两边应该都有变量，句子错误！');
                            exit = 1;
                            break;
                        }
                    } else if (judge === 8) {
                        // ) 若为右括号，这种情况一旦出现就要将(N)规约为N
                        // 判断前面是不是“(N”
                        if (Resource_Symbol[pScanStack - 1].syn == 13 && Resource_Symbol[pScanStack - 2].syn == 7) {
                            // 若前面为（N 则正确
                            // 语义分析,newtemp()
                            const temp_sym = new SymbolTable();
                            temp_sym.syn = 13;
                            // N<-(N),N.value=(N).value
                            temp_sym.value = Resource_Symbol[pScanStack - 1].value; // N中的值永远为真正的值，而不是地址
                            // 开始归约
                            Resource_Symbol.pop();
                            Resource_Symbol.pop();
                            Resource_Symbol.pop();               // 弹出三个元素
                            Resource_Symbol.push(temp_sym);      // 规约为N
                            pStackTop = Resource_Symbol.length - 1;
                            pScanStack = pStackTop - 1;         // 始终指向规约后的第一个非终结符
                            if (pScanStack < 0) {
                                console.log('句子错误，程序结束!');
                                exit = 1;
                                break;
                            }
                            row_prior = Search_Priority_Setxy(SearchSyn(Resource_Symbol[pScanStack].syn));
                            Prior_Relation = PriorityTable[row_prior][column_prior]; // 进行下次判断
                            console.log(`${Statement_count++}.此处为)，将(N)规约为N,并进行语义分析！`);
                            Print_Context(Resource_Symbol.length - 1, Sym_scan_pointer);
                        } else {
                            console.log('括号不匹配，句子错误！');
                            exit = 1;
                            break;
                        }
                    } else if (judge === 2) {
                        // ? 则问号前面必为N
                        if (Resource_Symbol[pScanStack - 1].syn == 13) {
                            // 语义分析,newtemp()
                            const temp_sym = new SymbolTable();
                            temp_sym.syn = 13;
                            temp_sym.value = Resource_Symbol[pScanStack - 1].value;
                            //开始归约
                            Resource_Symbol.pop();
                            Resource_Symbol.pop();                   // 弹出2个元素
                            Resource_Symbol.push(temp_sym);          // 规约为N
                            pStackTop = Resource_Symbol.length - 1;
                            pScanStack = pStackTop - 1;             // 始终指向规约后的第一个非终结符
                            if (pScanStack < 0) {
                                console.log('句子错误，程序结束!');
                                exit = 1;
                                break;
                            }
                            row_prior = Search_Priority_Setxy(SearchSyn(Resource_Symbol[pScanStack].syn));
                            Prior_Relation = PriorityTable[row_prior][column_prior]; // 进行下次判断
                            console.log(`${Statement_count++}.此处将 N? 规约为N`);
                            Print_Context(Resource_Symbol.length - 1, Sym_scan_pointer);
                        } else {
                            console.log('问号前面必须有N！');
                            exit = 1;
                            break;
                        }
                    } else {
                        console.log('暂时还未定义');
                        exit = 1;
                        break;
                    }
                }
            }

            if (exit === 1) break;
        }
    }
}
